/**
  ******************************************************************************
  * @author     Chris
  * @since      2024/6/24 21:20
  *
  * @file       hd_24cxx.c
  * @brief      24CXX hardware driver.
  *
  * @note       This file contains the hardware driver for the 24CXX.
  *
  * @warning    None.
  ******************************************************************************
  * Change Logs:
  *   Date          Author       Notes
  *   2024/6/24     Chris        the first version
  *
  ******************************************************************************
  */

#include "hd_24cxx.h"

static void FT24Cxx_init_(const FT24Cxx *this) {
}

static void FT24Cxx_read_(const FT24Cxx *this, uint16_t address, uint8_t *dat, uint8_t len) {
    I2C_read(this->i2c, FT24CXX_ADDRESS, address, dat, len);
}

static void FT24Cxx_writePage_(const FT24Cxx *this, const uint32_t address, uint8_t *dat, uint16_t count) {
    // The 256K EEPROM are capable of 64-byte page write.
    I2C_send(this->i2c, FT24CXX_ADDRESS, address, dat, count);
}

uint8_t AT24CXX_FLASH_BUFFER[FT24CXX_PAGE_SIZE];

static void FT24Cxx_write_(const FT24Cxx *this, uint16_t address, uint8_t *dat, uint8_t count) {
    // The FT24C256A has 256K bits (32KB) EEPROM are capable of 64-byte page write.
    uint32_t pageIndex = address / FT24CXX_PAGE_SIZE;     // page index
    uint16_t pageOffset = address % FT24CXX_PAGE_SIZE;    // offset of page
    uint16_t pageRemain = FT24CXX_PAGE_SIZE - pageOffset; // remain + offset = AT24CXX_PAGE_SIZE

    if (count <= pageRemain) pageRemain = count; // offset + count <= AT24CXX_PAGE_SIZE

    while (1) {
        // read all bytes of this page
        this->vptr->read(this, pageIndex * FT24CXX_PAGE_SIZE, AT24CXX_FLASH_BUFFER, FT24CXX_PAGE_SIZE);

        for (uint16_t i = 0; i < pageRemain; ++i) AT24CXX_FLASH_BUFFER[i + pageOffset] = dat[i];
        FT24Cxx_writePage_(this, pageIndex * FT24CXX_PAGE_SIZE, AT24CXX_FLASH_BUFFER, FT24CXX_PAGE_SIZE);

        if (count == pageRemain) break; // write done, and exit

        pageIndex++;
        pageOffset = 0;
        dat += pageRemain;
        address += pageRemain;
        count -= pageRemain;
        if (count > FT24CXX_PAGE_SIZE) pageRemain = FT24CXX_PAGE_SIZE;
        else pageRemain = count;
    }
}

void FT24Cxx_constructor(FT24Cxx *this, I2C *i2c) {
    static struct FT24CxxVtbl vtbl = {
        .init = &FT24Cxx_init_,
        .read = &FT24Cxx_read_,
        .write = &FT24Cxx_write_,
    };

    this->vptr = &vtbl;
    this->i2c = i2c;
}
